package Basics;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

public class Provenance 
{
	public static Provenance  prov = null;
	
	Map<Atom, HashSet<Body>> provenance;
	
	
	public Provenance () 
	{
		provenance = new HashMap<Atom, HashSet<Body>>();
	}
	
	
	public static Provenance getInstance() 
	{
		if(prov == null) 
		{
			prov = new Provenance();
		}
		
		return prov;
	}
	
	
	
	
	public Map<Atom, HashSet<Body>> getProvenance() 
	{
		return provenance;
	}


	public void setProvenance(Map<Atom, HashSet<Body>> provenance) 
	{
		this.provenance = provenance;
	}


	/*************************************************************************************************************/
	/** Title: AddRuleToProv																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddRuleToProv (Rule rule)
	{
		if (false == KeyMap.getInstance().getMap().containsKey(rule.getHead().toString()))
		{
			Update(rule.getHead());
			KeyMap.getInstance().Update(rule.getHead());
		}
		
		if (false == rule.getHead().TreesContainBody(rule.getBody()))
		{
			Body pointerBody = new Body();
			
			for (Atom atom : rule.getBody().getAtoms()) 
			{
				if (true == atom.isFact() && false == KeyMap.getInstance().getMap().containsKey(atom.toString()))
				{
					Update(atom);
					KeyMap.getInstance().Update(atom);
				}
				
				pointerBody.getAtoms().add(KeyMap.getInstance().getMap().get(atom.toString()));
			}
			
			pointerBody.setRuleUsed(rule.getBody().getRuleUsed());
			
			this.provenance.get(rule.getHead()).add(pointerBody);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: AddBodyToProv																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddBodyToProv (Atom key, Body body, Set<Atom> needToDerive)
	{
		Body pointerBody = new Body();
		for (Atom atom : body.getAtoms()) 
		{
			Update(atom, needToDerive);
			KeyMap.getInstance().Update(atom);

			pointerBody.getAtoms().add(KeyMap.getInstance().Get(atom));
			KeyMap.getInstance().Get(atom).setFact(atom.isFact());
			KeyMap.getInstance().Get(atom).setStable(atom.isStable());
		}

		this.provenance.get(key).add(pointerBody);
		pointerBody.setRuleUsed(body.getRuleUsed());
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Update																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void Update (Atom atom)
	{
		if (false == this.provenance.containsKey(atom)) 
		{
			this.provenance.put(atom, new HashSet<Body>());
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Update																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void Update (Atom atom, Set<Atom> needToDerive)
	{
		if (false == this.provenance.containsKey(atom)) 
		{
			this.provenance.put(atom, new HashSet<Body>());
			if (false == atom.isFact()) 
			{
				needToDerive.add(atom);
			}
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Update																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void UpdateDerivs (Atom atom, HashSet<Body> derivs)
	{
		this.provenance.put(atom, derivs);
	}
	
	
	/*************************************************************************************************************/
	/** Title: Update																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public void AddDeriv (Atom atom, Body body)
	{
		if (false == this.provenance.get(atom).contains(body)) 
		{
			this.provenance.get(atom).add(body);
		}
	}
	
	
	
	/*************************************************************************************************************/
	/** Title: Get																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public HashSet<Body> Get (Atom key)
	{
		return this.provenance.get(key);
	}
	
	
	/*************************************************************************************************************/
	/** Title: KeySet																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public Set<Atom> KeySet ()
	{
		return this.provenance.keySet();
	}
	
	
	/*************************************************************************************************************/
    /** Title: Values                                                                                                                                                               
    /** Description:                      
    /*************************************************************************************************************/
    
    public Collection<HashSet<Body>> Values ()
    {
    	return this.provenance.values();
    }
	
	
	/*************************************************************************************************************/
	/** Title: Reset																				
	/** Description: Empty all provenance info			
	/*************************************************************************************************************/

	public void Reset ()
	{
		this.provenance.clear();
	}
	
	

	
	
	/*************************************************************************************************************/
	/** Title: GetProvSize																				
	/** Description: 				
	/*************************************************************************************************************/
	
	public int GetProvSize ()
	{
		int size = 0;
		for (HashSet<Body> set : Provenance.getInstance().Values()) 
		{
			size += set.size();
		}
		
		return size;
	}
}
